#include "mainwindow.h"
#include "certificatedialog.h"
#include "ui_mainwindow.h"

#include <QMessageBox>
#include <QOpcUaErrorState>
#include <QOpcUaAuthenticationInformation>
#include <QJsonObject>
#include <QFileDialog>
#include <QButtonGroup>

#define COLUMN_ENABLE   0
#define COLUMN_BROWNAME 1
#define COLUMN_NODEID   2
#define COLUMN_VALUE    3
#define COLUMN_CONDITION 4
#define COLUMN_CONDVALUE 5
#define COLUMN_VOICETXT  6
#define CHARTVIEW_WIDTH 220
#define CHARTVIEW_HEIGHT 160
#define CHARTVIEW_SPACING 9

using namespace Qt::StringLiterals;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      ui(new Ui::MainWindow),
      m_provider(new QOpcUaProvider(this)),
      m_model(new QStandardItemModel(this)),
      m_selection(new QItemSelectionModel(m_model)) {

    ui->setupUi(this);
    this->setWindowTitle("数据监控");
    ui->label_QT_VERSION->setText(QString("%1. Based on Qt %2").arg("0.11 Beta").arg(QT_VERSION_STR));

    // 隐藏的可视组件
    ui->btn_clock->setVisible(false);

    // 过滤调试信息设置
    QLoggingCategory::setFilterRules(u"qt.opcua.plugins.open62541=true \n qt.opcua.plugins.open62541.*=false"_s);

    // 状态栏
    label_date = new QLabel("");
    label_date->setMinimumWidth(200);
    statusBar()->addPermanentWidget(label_date);

    connectIco = new QPixmap(":/wifi.svg");
    disconnectIco = new QPixmap(":/wifi-off.svg");
    speakIco = new QPixmap(":/volume-2.svg");
    speak = new QPixmap(":/volume.svg");
    speak_pasue = new QPixmap(":/volume-x.svg");
    label_speak.setMaximumSize(16, 16);
    label_speak.setScaledContents(true);
    label_speak.setPixmap(*speak);
    label_speak.setToolTip("语音播报状态");
    statusBar()->addPermanentWidget(&label_speak);

    label_pixmap.setMaximumSize(16, 16);
    label_pixmap.setScaledContents(true);
    label_pixmap.setPixmap(*disconnectIco);
    label_pixmap.setToolTip("服务器未连接");
    statusBar()->addPermanentWidget(&label_pixmap);

    QLabel* Copyright = new QLabel("<font>Copyright&#169; 2024</font> 设备部自动化室冶炼组", this);
    Copyright->setMinimumWidth(220);
    Copyright->setAlignment(Qt::AlignRight);  //水平居中(HCenter)。
    Copyright->setToolTip("武钢有限设备管理部 - 自动化室");
    statusBar()->addPermanentWidget(Copyright); //现实永久信息

    // 语音引擎功能初始化
    ui->engine->addItem(u"Default"_s, u"default"_s);
    const auto engines = QTextToSpeech::availableEngines();
    for (const QString &engine : engines)
        ui->engine->addItem(engine, engine);
    ui->engine->setCurrentIndex(0);
    engineSelected(0);
    connect(ui->rate, &QSlider::valueChanged, this, &MainWindow::setRate);
    connect(ui->volume, &QSlider::valueChanged, this, &MainWindow::setVolume);
    connect(ui->engine, &QComboBox::currentIndexChanged, this, &MainWindow::engineSelected);
    connect(ui->language, &QComboBox::currentIndexChanged, this, &MainWindow::languageSelected);
    connect(ui->voice, &QComboBox::currentIndexChanged, this, &MainWindow::voiceSelected);

    connectToDatabase();

    // tableView_monitoring初始化
    ui->view_monitoring->setModel(m_model);
    ui->view_monitoring->setSelectionModel(m_selection);
    connect(m_selection, &QItemSelectionModel::currentRowChanged, this, [ = ](const QModelIndex & current, const QModelIndex & previous) {
        Q_UNUSED(current);
        Q_UNUSED(previous);
        // 获取选中的行索引列表
        QList<QModelIndex> selectedRows = m_selection->selectedRows();
        ui->btn_deleteitem->setEnabled(selectedRows.size() == 1); // 选择了整行，可以进行删除操作

    });
    // 设置tableView编辑代理组件
    ui->view_monitoring->setItemDelegateForColumn(COLUMN_CONDITION, &comboBoxDelegate);
    ui->view_monitoring->setItemDelegateForColumn(COLUMN_CONDVALUE, &doubleSpinDelegate);
    ui->view_monitoring->setItemDelegateForColumn(COLUMN_VOICETXT, &textEditDelegate);
    m_model->setHorizontalHeaderLabels(QStringList() << "激活" << "项目名" << "节点" << "当前值" << "报警条件" << "报警值" << "播报内容");
    mContextMenu = new QMenu(ui->view_monitoring);
    mContextMenuShowChartAction = mContextMenu->addAction("显示在图表页", this, &MainWindow::doShowChart);
    ui->view_monitoring->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->view_monitoring, &QTableView::customContextMenuRequested, this, &MainWindow::openCustomContextMenu);

    // tableView_history初始化
    ui->view_history->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不可编辑
    ui->view_history->setSelectionBehavior(QAbstractItemView::SelectRows); // 行选择模式
    ui->view_history->setSelectionMode(QAbstractItemView::SingleSelection); // 单行选择
    ui->view_history->setItemDelegateForColumn(4, &lineEditDelegate);
    ui->view_history->setModel(&qryModel);
    ui->spinBox_rec_count->setValue(history_rec_count_page);
    connect(ui->comboBox_nodeid, &historyComboBox::combobox_showpopup, this, &MainWindow::refreshNodeId);

    // OPCUA设置页面按键信号
    connect(ui->btn_find_opc_servers, &QPushButton::clicked, this, &MainWindow::findServers);
    connect(ui->lineEdit_opcServerUrl, &QLineEdit::returnPressed, ui->btn_find_opc_servers, [this]() {
        QString url = ui->lineEdit_opcServerUrl->text();
        ui->lineEdit_opcServerUrl->setText(url.replace("：", ":"));
        this->ui->btn_find_opc_servers->animateClick();
    });
    connect(ui->btn_get_opc_endpoints, &QPushButton::clicked, this, &MainWindow::getEndpoints);
    connect(ui->btn_connect_to_opcserver, &QPushButton::clicked, this, &MainWindow::connectToServer);
    ui->lineEdit_opcServerUrl->setText(opc_server_url);

    // 首次执行自动连接opc_server_url服务器,首次执行标志：bool firstRun。
    //   第1步：获取目标服务器的OPCUA服务列表；
    //   第2步：获取选定OPCUA服务的安全认证模式；
    //   第3步：根据选定的安全认证模式连接OPCUA服务
    firstRun = true;
    findServers();

    // 进入主页面前，将当前页面调整到项目监控页面+初始化一些参数
    on_btn_opcua_clicked();
    on_btn_itemMonitoring_clicked();
    ui->toolBox->setCurrentWidget(ui->page_monitoring);
    // ui->horizontalLayout->setProperty("from", "title");

    updateUiState();
}
void MainWindow::openCustomContextMenu(const QPoint &point) {
    QModelIndex t_index = ui->view_monitoring->indexAt(point);
    QModelIndex t_value = t_index.siblingAtColumn(COLUMN_VALUE);
    if (t_value.isValid()) {
        QStandardItem* item = m_model->itemFromIndex(t_value);
        if (item) {
            // QVariant v_chartViewPointor = item->data(Qt::UserRole + 4);
            // FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
            QVariant v_itemPointor = QVariant::fromValue((void *) item);
            mContextMenuShowChartAction->setData(v_itemPointor);// 将t_NodePointor指针存入mContextMenuMonitoringAction中，然后在toggleMonitoring()槽函数中取用

            bool showFlag = item->data(Qt::UserRole + 5).toBool();
            mContextMenuShowChartAction->setText(showFlag ? tr("不显示在图表页") : tr("显示在图表页"));

            mContextMenu->exec(ui->view_monitoring->viewport()->mapToGlobal(point));
        }
    }
}

//! [1.语音功能部分]
void MainWindow::setRate(int rate) {
    m_speech->setRate(rate / 10.0);
    ui->saveButton->setEnabled(true);
}
void MainWindow::setVolume(int volume) {
    m_speech->setVolume(volume / 100.0);
    ui->saveButton->setEnabled(true);
}
void MainWindow::stateChanged(QTextToSpeech::State state) {
    switch (state) {
    case QTextToSpeech::Speaking:
        label_speak.setPixmap(*speakIco);
        break;
    case QTextToSpeech::Ready:
        label_speak.setPixmap(QPixmap(""));
        break;
    case QTextToSpeech::Paused:
        label_speak.setPixmap(*speak_pasue);
        break;
    default:
        label_speak.setPixmap(*speak);
        break;
    }

    ui->pauseButton->setEnabled(state == QTextToSpeech::Speaking);
    ui->resumeButton->setEnabled(state == QTextToSpeech::Paused);
    ui->stopButton->setEnabled(state == QTextToSpeech::Speaking || state == QTextToSpeech::Paused);
}
void MainWindow::engineSelected(int index) {
    ui->engine->setEnabled(false);

    const QString engineName = ui->engine->itemData(index).toString();
    delete m_speech;
    m_speech = engineName == u"default"
               ? new QTextToSpeech(this)
               : new QTextToSpeech(engineName, this);

    // 一些引擎异步初始化
    if (m_speech->state() == QTextToSpeech::Ready) {
        onEngineReady();
    } else {
        connect(m_speech, &QTextToSpeech::stateChanged, this, &MainWindow::onEngineReady,
                Qt::SingleShotConnection);
    }
    ui->saveButton->setEnabled(true);
}
void MainWindow::onEngineReady() {
    ui->engine->setEnabled(true);
    if (m_speech->state() != QTextToSpeech::Ready) {
        stateChanged(m_speech->state());
        return;
    }

    const bool hasPauseResume = m_speech->engineCapabilities()
                                & QTextToSpeech::Capability::PauseResume;
    ui->pauseButton->setVisible(hasPauseResume);
    ui->resumeButton->setVisible(hasPauseResume);

    // 填充时阻止语言组合框的信号
    QSignalBlocker blocker(ui->language);

    ui->language->clear();
    const QList<QLocale> locales = m_speech->availableLocales();
    QLocale current = m_speech->locale();
    qDebug() << "1.language.current.name" << m_speech->locale().name();
    qDebug() << "2.voice.current.name" << m_speech->voice().name();

    for (const QLocale &locale : locales) {
        QString name(QString("%1 (%2)")
                     .arg(QLocale::languageToString(locale.language()),
                          QLocale::territoryToString(locale.territory())));
        QVariant localeVariant(locale);
        ui->language->addItem(name, localeVariant);
        if (locale.name() == current.name())
            current = locale;
    }
    setRate(ui->rate->value());
    setVolume(ui->volume->value());
    //! [say]
    connect(ui->speakButton, &QPushButton::clicked, m_speech, [this] {
        m_speech->say(ui->textEdit_voice->toPlainText());
    });
    //! [say]
    //! [stop]
    connect(ui->stopButton, &QPushButton::clicked, m_speech, [this] {
        m_speech->stop();
    });
    //! [stop]
    //! [pause]
    connect(ui->pauseButton, &QPushButton::clicked, m_speech, [this] {
        m_speech->pause();
    });
    //! [pause]
    //! [resume]
    connect(ui->resumeButton, &QPushButton::clicked, m_speech, &QTextToSpeech::resume);
    //! [resume]

    connect(m_speech, &QTextToSpeech::stateChanged, this, &MainWindow::stateChanged);
    connect(m_speech, &QTextToSpeech::localeChanged, this, &MainWindow::localeChanged);

    blocker.unblock();

    localeChanged(current);
}
void MainWindow::languageSelected(int language) {
    QLocale locale = ui->language->itemData(language).toLocale();
    m_speech->setLocale(locale);
    ui->saveButton->setEnabled(true);
}
void MainWindow::voiceSelected(int index) {
    m_speech->setVoice(m_voices.at(index));
    ui->saveButton->setEnabled(true);
}
void MainWindow::localeChanged(const QLocale &locale) {
    QVariant localeVariant(locale);
    ui->language->setCurrentIndex(ui->language->findData(localeVariant));

    QSignalBlocker blocker(ui->voice);

    ui->voice->clear();

    m_voices = m_speech->availableVoices();
    QVoice currentVoice = m_speech->voice();
    qDebug() << "3.language.current.name" << m_speech->locale().name();
    qDebug() << "4.voice.current.name" << m_speech->voice().name();
    for (const QVoice &voice : std::as_const(m_voices)) {
        ui->voice->addItem(QString("%1 - %2 - %3")
                           .arg(voice.name(), QVoice::genderName(voice.gender()),
                                QVoice::ageName(voice.age())));
        if (voice.name() == currentVoice.name())
            ui->voice->setCurrentIndex(ui->voice->count() - 1);
    }
    ui->saveButton->setEnabled(true);
}
void MainWindow::on_saveButton_clicked() {
    if(!db.isOpen())
        return;

    QSqlQuery query;
    bool r1 = query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'volume'").arg(ui->volume->value()));
    bool r2 = query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'rate'").arg(ui->rate->value()));
    bool r3 = query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'language'").arg(ui->language->currentText()));
    bool r4 = query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'voice'").arg(ui->voice->currentText()));

    if(!r1 || !r2 || !r3 || !r4)
        QMessageBox::warning(this, "出错", "服务器信息保存失败！", QMessageBox::Ok);
    else
        QMessageBox::information(this, "提示", "服务器信息保存成功！", QMessageBox::Ok);
    ui->saveButton->setEnabled(false);
}
//! [1.语音功能部分]

//! [2.主页面切换功能]
void MainWindow::on_btn_itemMonitoring_clicked() {
    ui->stackedWidget->setCurrentWidget(ui->page_tableview);
    QFont f;
    f.setBold(true);
    f.setItalic(true);
    ui->btn_itemMonitoring->setFont(f);

    f.setBold(false);
    f.setItalic(false);
    ui->btn_itemHistory->setFont(f);
    ui->btn_clock->setFont(f);
    ui->btn_chart->setFont(f);
    toolbox_monitoring_current_index = 0;
    refreshChartView();
}
void MainWindow::on_btn_itemHistory_clicked() {
    ui->stackedWidget->setCurrentWidget(ui->page_history);
    QFont f;
    f.setBold(true);
    f.setItalic(true);
    ui->btn_itemHistory->setFont(f);

    f.setBold(false);
    f.setItalic(false);
    ui->btn_itemMonitoring->setFont(f);
    ui->btn_clock->setFont(f);
    ui->btn_chart->setFont(f);
    toolbox_monitoring_current_index = 1;
    QDateTime t = QDateTime::currentDateTime();
    ui->dateBegin->setDateTime(t.addMonths(-1));
    ui->dateEnd->setDateTime(t);
}
void MainWindow::on_btn_chart_clicked() {
    ui->stackedWidget->setCurrentWidget(ui->page_chart);
    QFont f;
    f.setBold(true);
    f.setItalic(true);
    ui->btn_chart->setFont(f);

    f.setBold(false);
    f.setItalic(false);
    ui->btn_itemMonitoring->setFont(f);
    ui->btn_itemHistory->setFont(f);
    ui->btn_clock->setFont(f);
    toolbox_monitoring_current_index = 3;
    refreshChartView();
    adjustLayout();
}
void MainWindow::on_btn_clock_clicked() {
    ui->stackedWidget->setCurrentWidget(ui->page_clock);
    QFont f;
    f.setBold(true);
    f.setItalic(true);
    ui->btn_clock->setFont(f);

    f.setBold(false);
    f.setItalic(false);
    ui->btn_itemMonitoring->setFont(f);
    ui->btn_itemHistory->setFont(f);
    ui->btn_chart->setFont(f);
    toolbox_monitoring_current_index = 2;
}
void MainWindow::on_btn_opcua_clicked() {
    ui->stackedWidget->setCurrentWidget(ui->page_opcua);
    QFont f;
    f.setBold(true);
    f.setItalic(true);
    ui->btn_opcua->setFont(f);
    f.setBold(false);
    f.setItalic(false);
    ui->btn_voice->setFont(f);
    ui->btn_other->setFont(f);
    toolbox_setting_current_index = 0;
}
void MainWindow::on_btn_voice_clicked() {
    ui->stackedWidget->setCurrentWidget(ui->page_voice);
    QFont f;
    f.setBold(true);
    f.setItalic(true);
    ui->btn_voice->setFont(f);
    f.setBold(false);
    f.setItalic(false);
    ui->btn_opcua->setFont(f);
    ui->btn_other->setFont(f);
    toolbox_setting_current_index = 1;
}
void MainWindow::on_btn_other_clicked() {
    ui->stackedWidget->setCurrentWidget(ui->page_other);
    ui->spinBox_point_count_x->setValue(point_count_x);
    ui->spinBox_say_max_count->setValue(say_max_count);

    QFont f;
    f.setBold(true);
    f.setItalic(true);
    ui->btn_other->setFont(f);

    f.setBold(false);
    f.setItalic(false);
    ui->btn_opcua->setFont(f);
    ui->btn_voice->setFont(f);
    toolbox_setting_current_index = 2;
}
void MainWindow::on_toolBox_currentChanged(int index) {
    switch (index) {
    case 0:
        switch (toolbox_monitoring_current_index) {
        case 0:
            on_btn_itemMonitoring_clicked();
            break;
        case 1:
            on_btn_itemHistory_clicked();
            break;
        case 2:
            on_btn_clock_clicked();
            break;
        case 3:
            on_btn_chart_clicked();
            break;
        }
        break;
    case 1:
        switch (toolbox_setting_current_index) {
        case 0:
            on_btn_opcua_clicked();
            break;
        case 1:
            on_btn_voice_clicked();
            break;
        case 2:
            on_btn_other_clicked();
            break;
        }
        break;
    }
}
//! [2.主页面切换功能]

//! [3.数据库功能]
void MainWindow::connectToDatabase() {
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(QCoreApplication::applicationDirPath() + "/db.sqlite3");

    if(!db.open())
        return;

    QSqlQuery query;
    // 创建报警项目表
    query.exec("CREATE TABLE IF NOT EXISTS tableview("
               "    ID integer primary key AUTOINCREMENT NOT NULL,"
               "    激活 integer NOT NULL DEFAULT 0,"
               "    监控项目 varchar(50) NOT NULL,"
               "    节点 varchar(100) NOT NULL,"
               "    报警条件 varchar(10) NOT NULL,"
               "    报警值 varchar(50) NOT NULL,"
               "    刷新时间 interval NOT NULL DEFAULT 1000,"
               "    播报内容 varchar(250),"
               "    显示 integer NOT NULL DEFAULT 0)");
    // 创建报警记录表
    query.exec("CREATE TABLE IF NOT EXISTS alarm("
               "    ID integer primary key AUTOINCREMENT NOT NULL,"
               "    时间 varchar(50) NOT NULL,"
               "    监控项目 varchar(50) NOT NULL,"
               "    节点 varchar(100) NOT NULL,"
               "    报警内容 varchar(250))");
    // 创建报警记录表
    query.exec("CREATE TABLE IF NOT EXISTS sysinfo("
               "    ID integer primary key AUTOINCREMENT NOT NULL,"
               "    参数 varchar(20) NOT NULL,"
               "    值 varchar(100))");

    // 1. 从数据库中获取OPCUA服务器地址
    query.exec("select 值 from sysinfo where 参数='opc_tcp'");
    query.last();
    int count = query.at() + 1;
    if(count > 0) {
        query.first();
        opc_server_url = query.value(0).toString();
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('opc_tcp', 'opc.tcp://127.0.0.1:49320')");
        opc_server_url = "opc.tcp://127.0.0.1:49320";
    }

    // 2. 从数据库中获取OPCUA服务的序号
    query.exec("select 值 from sysinfo where 参数='opc_server_index'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        opc_server_index = query.value(0).toInt();
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('opc_server_index', '0')");
        opc_server_index = 0;
    }

    // 3. 从数据库中获取OPCUA安全模式的序号
    query.exec("select 值 from sysinfo where 参数='opc_endpoint_index'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        opc_endpoint_index = query.value(0).toInt();
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('opc_endpoint_index', '0')");
        opc_endpoint_index = 0;
    }

    // 5. 从数据库中获取volume
    query.exec("select 值 from sysinfo where 参数='volume'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        ui->volume->setValue(query.value(0).toInt());
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('volume', '50')");
        ui->volume->setValue(50);
    }

    // 6. 从数据库中获取 rate
    query.exec("select 值 from sysinfo where 参数='rate'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        ui->rate->setValue(query.value(0).toInt());
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('rate', '0')");
        ui->rate->setValue(0);
    }

    // 7. 从数据库中获取 point_count_x
    query.exec("select 值 from sysinfo where 参数='point_count_x'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        point_count_x = query.value(0).toInt();
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('point_count_x', '60')");
        point_count_x = 60;
    }

    // 4. 从数据库中获取 language
    query.exec("select 值 from sysinfo where 参数='language'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        ui->language->setCurrentText(query.value(0).toString());
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('language', 'Chinese (China)')");
        ui->language->setCurrentText("Chinese (China)");
    }

    // 8. 从数据库中获取 voice
    query.exec("select 值 from sysinfo where 参数='voice'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        ui->voice->setCurrentText(query.value(0).toString());
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('voice', 'Microsoft Huihui Desktop - Female - Adult')");
        ui->voice->setCurrentText("Microsoft Huihui Desktop - Female - Adult");
    }

    // 9. 从数据库中获取 say_max_count
    query.exec("select 值 from sysinfo where 参数='say_max_count'");
    query.last();
    count = query.at() + 1;
    if(count > 0) {
        query.first();
        say_max_count = query.value(0).toInt();
    } else {
        query.exec("INSERT INTO SYSINFO(参数, 值)"
                   "VALUES ('say_max_count', '1')");
        say_max_count = 1;
    }
}
void MainWindow::addAlarmInfo(QString browseName, QString nodeId, QString info) {
    if(!db.isOpen())
        return;

    QDateTime dateTime = QDateTime::currentDateTime(); //获取系统当前的时间
    opc_server_time = dateTime .toString("yyyy-MM-dd hh:mm:ss");//格式化时间

    QSqlQuery query;
    query.exec(QString("INSERT INTO alarm (时间, 监控项目, 节点, 报警内容) "
                       "VALUES ('%1', '%2', '%3', '%4')")
               .arg(opc_server_time, browseName, nodeId, info));
    // qDebug() << query.executedQuery();
}
void MainWindow::showHistory() {
    QString condition = QString(" WHERE (时间 BETWEEN '%1' AND '%2') ")
                        .arg(ui->dateBegin->dateTime().toString("yyyy-MM-dd hh:mm:ss"))
                        .arg(ui->dateEnd->dateTime().toString("yyyy-MM-dd hh:mm:ss"));
    if(!ui->lineEdit_browname->text().isEmpty())
        condition.append(QString("AND (监控项目 LIKE '%%1%')").arg(ui->lineEdit_browname->text()));
    if(!ui->comboBox_nodeid->currentText().isEmpty())
        condition.append(QString("AND (节点 LIKE '%%1%')").arg(ui->comboBox_nodeid->currentText()));

    // 获取总记录数
    QSqlQuery query;
    query.exec("select count(ID) from alarm" + condition);
    query.first();
    int rec_count = query.value(0).toInt();


    condition.append(QString(" ORDER BY 时间 DESC limit %1, %2 ")
                     .arg((history_current_page - 1) * history_rec_count_page)
                     .arg(history_rec_count_page));

    QString sql_str = QString("select * from alarm");
    sql_str.append(condition);
    // qDebug() << sql_str;
    qryModel.setQuery(sql_str);
    qryModel.setHeaderData(0, Qt::Horizontal, "序号");
    qryModel.setHeaderData(1, Qt::Horizontal, "时间");
    qryModel.setHeaderData(2, Qt::Horizontal, "项目名");
    qryModel.setHeaderData(3, Qt::Horizontal, "节点");
    qryModel.setHeaderData(4, Qt::Horizontal, "报警内容");
    ui->view_history->resizeColumnsToContents();

    if( rec_count % history_rec_count_page > 0 )
        history_pages = rec_count / history_rec_count_page + 1;
    else
        history_pages = rec_count / history_rec_count_page;

    ui->label_history_info->setText(QString("共%3条记录，第%1页/共%2页，每页").arg(history_current_page).arg(history_pages).arg(rec_count));
    updateHistoryBtnState();
}
void MainWindow::refreshNodeId() {
    QSqlQuery query;
    query.exec("SELECT DISTINCT 节点 FROM alarm ORDER BY 节点");
    ui->comboBox_nodeid->clear();
    ui->comboBox_nodeid->addItem("");
    while (query.next()) {
        ui->comboBox_nodeid->addItem(query.value(0).toString());
    }
}
void MainWindow::updateHistoryBtnState() {
    ui->btn_page_frist->setEnabled(history_current_page != 1);
    ui->btn_page_pre->setEnabled(history_current_page > 1);
    ui->btn_page_next->setEnabled(history_current_page < history_pages);
    ui->btn_page_end->setEnabled(history_current_page < history_pages);
}
void MainWindow::saveTableView() {
    if(!db.isOpen())
        return;

    QSqlQuery query;

    // 清空tableview表格数据
    query.exec("delete from tableview");

    // 将tableview表格数据存入数据库
    for(int r = 0; r < m_model->rowCount(); r++) {
        bool enable       = m_model->item(r, COLUMN_ENABLE)->checkState() == Qt::Checked;
        QString browName  = m_model->item(r, COLUMN_BROWNAME)->text();
        QString nodeId    = m_model->item(r, COLUMN_NODEID)->text();
        QString condition = m_model->item(r, COLUMN_CONDITION)->text();
        QString condValue = m_model->item(r, COLUMN_CONDVALUE)->text();
        QString voiceTxt  = m_model->item(r, COLUMN_VOICETXT)->text();
        int interval      = m_model->item(r, COLUMN_VALUE)->data(Qt::UserRole + 2).toInt();
        int showFlag      = m_model->item(r, COLUMN_VALUE)->data(Qt::UserRole + 5).toInt();

        query.exec(QString("INSERT INTO tableview (激活, 监控项目, 节点, 报警条件, 报警值, 播报内容, 刷新时间, 显示) "
                           "VALUES (%1, '%2', '%3', '%4', '%5', '%6', %7, %8)")
                   .arg(enable).arg(browName, nodeId, condition, condValue, voiceTxt).arg(interval).arg(showFlag));
        // qDebug() << query.executedQuery();
    }
}
// 调用前，首先要连接到OPCUA服务器client、数据库服务器db
void MainWindow::loadTableView() {
    if(!db.isOpen()) {
        QMessageBox::warning(this, "错误提示", "数据服务器未连接！");
        return;
    }

    // 先清除已经订阅的数据，然后再重新从数据库载入
    clearItem();

    QSqlQuery query;
    if(query.exec("select * from tableview limit 0, 100")) {
        while (query.next()) {
            addItem(query.value("激活").toBool(),
                    query.value("监控项目").toString(),
                    query.value("节点").toString(),
                    query.value("报警条件").toString(),
                    query.value("报警值").toString(),
                    query.value("播报内容").toString(),
                    query.value("刷新时间").toString(),
                    query.value("显示").toBool());
        }
    }

    // 设置列宽
    ui->view_monitoring->resizeColumnsToContents();
    ui->view_monitoring->setColumnWidth(COLUMN_BROWNAME, 200);
    ui->view_monitoring->setColumnWidth(COLUMN_NODEID, 260);
    ui->view_monitoring->setColumnWidth(COLUMN_VALUE, 120);
    ui->view_monitoring->setColumnWidth(COLUMN_CONDITION, 60);
    ui->view_monitoring->setColumnWidth(COLUMN_CONDVALUE, 120);
    ui->view_monitoring->horizontalHeader()->setMaximumSectionSize(500);

    // 设置tableView数据变化的信号函数，这个是model的信号函数
    connect(m_model, &QStandardItemModel::itemChanged, this, &MainWindow::doItemChanged);
}
void MainWindow::on_btn_save_opcserver_parameter_clicked() {
    if(!db.isOpen())
        return;

    QSqlQuery query;
    QString url = ui->lineEdit_opcServerUrl->text();
    if(url.isEmpty())
        url = "opc.tcp://127.0.0.1:49320";
    bool r1 = query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'opc_tcp'").arg(url));

    int i = ui->comboBox_opcServers->currentIndex();
    if(i < 0) i = 0;
    bool r2 = query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'opc_server_index'").arg(i));

    i = ui->comboBox_opcEndPoints->currentIndex();
    if(i < 0) i = 0;
    bool r3 = query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'opc_endpoint_index'").arg(i));

    if(!r1 || !r2 || !r3)
        QMessageBox::warning(this, "出错", "服务器信息保存失败！", QMessageBox::Ok);
    else
        QMessageBox::information(this, "提示", "服务器信息保存成功！", QMessageBox::Ok);
}

//! [3.数据库功能]

//! [4.OPCUA服务器功能]
void MainWindow::connectToServer() {
    if (mClientConnected) {
        m_client->disconnectFromEndpoint();
        return;
    }

    if (ui->comboBox_opcEndPoints->currentIndex() >= 0) {
        m_endpoint = mEndpointList[ui->comboBox_opcEndPoints->currentIndex()];
        createOpcuaClient();
        m_client->connectToEndpoint(m_endpoint);
        qInfo() << QString("四、通过 %1 认证模式连接OPCUA服务.").arg(m_endpoint.securityPolicy());
    }
}
void MainWindow::createOpcuaClient() {
    QStringList m_backends = m_provider->availableBackends();
    if (m_backends.isEmpty()) {
        QMessageBox::critical(this, tr("没有可用的OPCUA插件"), tr("可用OPCUA插件列表为空。无法连接。"));
        return;
    }
    if (m_client == nullptr) {
        m_client = m_provider->createClient(m_backends.first());
        if (!m_client) {
            const QString message(tr("连接到给定服务器失败。有关详细信息，请参阅日志。"));
            QMessageBox::critical(this, tr("连接服务器失败"), message);
            return;
        }

        m_client->setApplicationIdentity(m_identity);
        m_client->setPkiConfiguration(m_pkiConfig);

        if (m_client->supportedUserTokenTypes().contains(QOpcUaUserTokenPolicy::TokenType::Certificate)) {
            QOpcUaAuthenticationInformation authInfo;
            authInfo.setCertificateAuthentication();
            m_client->setAuthenticationInformation(authInfo);
        }

        connect(m_client, &QOpcUaClient::connectError, this, &MainWindow::showErrorDialog);
        connect(m_client, &QOpcUaClient::connected, this, [ = ]() {
            mClientConnected = true;
            subscribeOpcUaDate();// 获取OPCUA服务器时间
            updateUiState();
        });
        connect(m_client, &QOpcUaClient::disconnected, this, [ = ]() {
            mClientConnected = false;
            m_client->deleteLater();
            m_client = nullptr;
            // mOpcUaModel->setOpcUaClient(nullptr);
            updateUiState();
        });
        connect(m_client, &QOpcUaClient::errorChanged, this, [ = ](QOpcUaClient::ClientError error) {
            qInfo() << "客户端错误:" << error;
        });
        connect(m_client, &QOpcUaClient::stateChanged, this, [ = ](QOpcUaClient::ClientState state) {
            if (state == QOpcUaClient::ClientState::Connected) {
                loadTableView();    // 4.4 OPCUA服务器连接成功后，就调入订阅的清单
                this->setWindowTitle("数据监控 - " + ui->lineEdit_opcServerUrl->text());
                ui->statusbar->showMessage("客户端状态:已连接服务器 " + ui->lineEdit_opcServerUrl->text());
                label_pixmap.setPixmap(*connectIco);
                label_pixmap.setToolTip("服务器已连接");
            }
            if (state == QOpcUaClient::ClientState::Disconnected) {
                this->setWindowTitle("数据监控 - 未连接服务器");
                ui->statusbar->showMessage("客户端状态:未连接服务器");
                label_pixmap.setPixmap(*disconnectIco);
                label_pixmap.setToolTip("服务器未连接");
            }
            if (state == QOpcUaClient::ClientState::Connecting) {
                this->setWindowTitle("数据监控 - 服务器连接中...");
                ui->statusbar->showMessage("客户端状态:服务器连接中...");
            }
            if (state == QOpcUaClient::ClientState::Closing) {
                this->setWindowTitle("数据监控 - 正在关闭");
                ui->statusbar->showMessage("客户端状态:正在关闭...");
            }
            qInfo() << "    客户端状态:" << state;
        });
        connect(m_client, &QOpcUaClient::endpointsRequestFinished, this, &MainWindow::getEndpointsComplete);
        connect(m_client, &QOpcUaClient::findServersFinished, this, &MainWindow::findServersComplete);
    }
}
void MainWindow::showErrorDialog(QOpcUaErrorState *errorState) {
    int result = 0;

    const QString statuscode = QOpcUa::statusToString(errorState->errorCode());

    QString msg = errorState->isClientSideError() ? tr("客户端错误: ") : tr("服务器端错误: ");

    switch (errorState->connectionStep()) {
    case QOpcUaErrorState::ConnectionStep::Unknown:
        break;
    case QOpcUaErrorState::ConnectionStep::CertificateValidation: {
        CertificateDialog dlg(this);
        msg += tr("服务器证书验证失败，错误码 0x%1（%2）。\n单击“Abort”以中止连接，或单击“Ignore”以继续连接.")
               .arg(static_cast<ulong>(errorState->errorCode()), 8, 16, QLatin1Char('0')).arg(statuscode);
        result = dlg.showCertificate(msg, m_endpoint.serverCertificate(), m_pkiConfig.trustListDirectory());
        errorState->setIgnoreError(result == 1);
    }
    break;
    case QOpcUaErrorState::ConnectionStep::OpenSecureChannel:
        msg += tr("打开安全通道失败,并出现错误 0x%1 (%2).").arg(errorState->errorCode(), 8, 16, QLatin1Char('0')).arg(statuscode);
        QMessageBox::warning(this, tr("连接错误"), msg);
        break;
    case QOpcUaErrorState::ConnectionStep::CreateSession:
        msg += tr("创建会话失败，并出现错误 0x%1 (%2).").arg(errorState->errorCode(), 8, 16, QLatin1Char('0')).arg(statuscode);
        QMessageBox::warning(this, tr("连接错误"), msg);
        break;
    case QOpcUaErrorState::ConnectionStep::ActivateSession:
        msg += tr("激活会话失败，并出现错误 0x%1 (%2).").arg(errorState->errorCode(), 8, 16, QLatin1Char('0')).arg(statuscode);
        QMessageBox::warning(this, tr("连接错误"), msg);
        break;
    }
}
void MainWindow::findServers() {
    QStringList localeIds;
    QStringList serverUris;
    QUrl url(ui->lineEdit_opcServerUrl->text());

    updateUiState();

    createOpcuaClient();
    // 如果缺少端口号，则设置默认端口
    if (url.port() == -1) url.setPort(49320);

    if (m_client) {
        qInfo() << "一、查询服务器" << url.toString() << "提供OPC服务的接口。";
        ui->statusbar->showMessage("一、查询服务器 " + url.toString() + " 提供OPC服务的接口。");
        ui->comboBox_opcServers->clear();
        ui->comboBox_opcEndPoints->clear();
        m_client->findServers(url, localeIds, serverUris);
    }
}
void MainWindow::findServersComplete(const QList<QOpcUaApplicationDescription> &servers, QOpcUa::UaStatusCode statusCode) {
    QOpcUaApplicationDescription server;

    if (isSuccessStatus(statusCode)) {
        int index = 0;
        qInfo() << "二、获得可连接的OPC服务接口:";
        for (const auto &server : servers) {
            const auto urls = server.discoveryUrls();
            for (const auto &url : std::as_const(urls)) {
                ui->comboBox_opcServers->addItem(url);
                qInfo() << QString("  %1.%2").arg(++index).arg(url);
            }
        }
        ui->statusbar->showMessage(QString("获得可连接的OPC服务接口：%1个").arg(index));
        if(opc_server_index < servers.data()->discoveryUrls().size())
            ui->comboBox_opcServers->setCurrentIndex(opc_server_index);

        if(firstRun)
            getEndpoints();

    } else {
        firstRun = false;
        ui->statusbar->showMessage("错误：没有从服务器上获得可用的OPC服务！" + statusToString(statusCode));
    }

    updateUiState();
}
void MainWindow::getEndpoints() {
    ui->comboBox_opcEndPoints->clear();

    if (ui->comboBox_opcServers->currentIndex() >= 0) {
        const QString serverUrl = ui->comboBox_opcServers->currentText();
        createOpcuaClient();
        m_client->requestEndpoints(serverUrl);
    }
}
void MainWindow::getEndpointsComplete(const QList<QOpcUaEndpointDescription> &endpoints, QOpcUa::UaStatusCode statusCode) {
    int index = 0;

    if (isSuccessStatus(statusCode)) {
        mEndpointList = endpoints;
        qInfo() << "三、该接口支持的安全认证模式:";
        for (const auto &endpoint : endpoints) {
            const QString EndpointName = QStringLiteral("%1").arg(endpoint.securityPolicy());
            ui->comboBox_opcEndPoints->addItem(EndpointName, index++);
            qInfo() << QString("  %1.%2").arg(index).arg(EndpointName);
        }
        ui->statusbar->showMessage(QString("获得该接口支持的安全认证模式：%1个").arg(index));
        if(opc_endpoint_index < endpoints.size())
            ui->comboBox_opcEndPoints->setCurrentIndex(opc_endpoint_index);

        if(firstRun) {
            connectToServer();
            firstRun = false;
        }
    } else {
        firstRun = false;
        ui->statusbar->showMessage("错误：没有从服务器上获得可用的安全连接模式！" + statusToString(statusCode));
    }

    updateUiState();
}
void MainWindow::updateUiState() {
    // 仅当尚未创建后端时，才允许更改后端
    ui->btn_connect_to_opcserver->setText(mClientConnected ? tr("断开") : tr("连接"));

    if (mClientConnected) {
        ui->lineEdit_opcServerUrl->setEnabled(false);
        ui->comboBox_opcServers->setEnabled(false);
        ui->comboBox_opcEndPoints->setEnabled(false);
        ui->btn_find_opc_servers->setEnabled(false);
        ui->btn_get_opc_endpoints->setEnabled(false);
        ui->btn_connect_to_opcserver->setEnabled(true);
    } else {
        ui->lineEdit_opcServerUrl->setEnabled(true);
        ui->comboBox_opcServers->setEnabled(ui->comboBox_opcServers->count() > 0);
        ui->comboBox_opcEndPoints->setEnabled(ui->comboBox_opcEndPoints->count() > 0);

        ui->btn_find_opc_servers->setDisabled(ui->lineEdit_opcServerUrl->text().isEmpty());
        ui->btn_get_opc_endpoints->setEnabled(ui->comboBox_opcServers->currentIndex() != -1);
        ui->btn_connect_to_opcserver->setEnabled(ui->comboBox_opcEndPoints->currentIndex() != -1);
    }

    if (!m_client) {
        ui->comboBox_opcServers->setEnabled(false);
        ui->comboBox_opcEndPoints->setEnabled(false);
        ui->btn_get_opc_endpoints->setEnabled(false);
        ui->btn_connect_to_opcserver->setEnabled(false);
    }

    ui->btn_save_opcserver_parameter->setEnabled(ui->comboBox_opcEndPoints->currentIndex() >= 0);
    // qDebug() << "First Run:" << firstRun;
}
// TableView当前值改变信号
void MainWindow::doItemChanged(QStandardItem * item) {
    bool en = m_model->item(item->row(), COLUMN_ENABLE)->data(Qt::CheckStateRole).toBool();

    // 判断是否是“激活”所在的列
    if(item->column() == COLUMN_ENABLE) {
        QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
        QVariant t_NodePointor    = item_value->data(Qt::UserRole + 1);
        int opc_interval          = item_value->data(Qt::UserRole + 2).toInt();

        // 激活或关闭本条记录的订阅
        if (t_NodePointor.isValid()) {
            QOpcUaNode *t_node = (QOpcUaNode*)t_NodePointor.value<void*>(); // 将指针转换为QOpcUaNode对象
            if (t_node) {
                QOpcUaMonitoringParameters monitoring = t_node->monitoringStatus(QOpcUa::NodeAttribute::Value);
                bool Reporting = monitoring.statusCode() == QOpcUa::UaStatusCode::Good &&
                                 monitoring.monitoringMode() == QOpcUaMonitoringParameters::MonitoringMode::Reporting;
                if(Reporting) {
                    t_node->disableMonitoring(QOpcUa::NodeAttribute::Value);
                    item_value->setData(false, Qt::UserRole + 3);
                    item_value->setIcon(QIcon());
                } else if(en)
                    t_node->enableMonitoring(QOpcUa::NodeAttribute::Value, opc_interval);
            }
        }
    }

    // 判断是否是“当前值”所在的列，监测实时数据是否满足报警条件
    if(item->column() == COLUMN_VALUE) {

        // 获取当前item里面保存的历史报警状态
        bool alarm_state = item->data(Qt::UserRole + 3).toBool();   // 首次报警标志
        QString voice_txt = m_model->item(item->row(), COLUMN_VOICETXT)->text();

        QString typeName = item->data(Qt::DisplayRole).metaType().name();

        if(item->data(Qt::DisplayRole).metaType().id() == QMetaType::Bool) {
//! bool类型值处理过程
            bool value = item->data(Qt::DisplayRole).toBool();
            bool condvalue  = item->index().siblingAtColumn(COLUMN_CONDVALUE).data().toBool();

            // 当报警条件成立，并且上次报警状态为假时，为首次报警，发出报警信号
            if(en && value == condvalue) {
                if(alarm_state == false) {  // 首次报警
                    // qDebug() << item <<  item->row() << item->column() << "发出报警！BOOL条件成立！";
                    addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                                 m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                                 QString("<font color=\"red\">当前值满足设定条件，触发报警，实时值=%1 (%3)，设定条件=%2</font>").arg(value).arg(condvalue).arg(typeName));
                    item->setData(true, Qt::UserRole + 3);
                    item->setIcon(QIcon(":/alarm-1.svg"));
                }

                QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                if(v_chartViewPointor.isValid()) {
                    FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                    if(v_chartView) {
                        v_chartView->setAlarmFlag(true);
                        int sc = v_chartView->getSayCount(); // 已播放次数
                        if(m_speech->state() == QTextToSpeech::Ready &&  sc < say_max_count) {
                            m_speech->say(voice_txt);
                            v_chartView->setSayCount(++sc); // 播放次数增1
                            // qDebug() << v_chartView << sc;
                        }
                    }
                }
            }

            // 当报警条件不成立，并且上次报警状态为真时，为首次解除报警，发出解除报警信号
            if(en && value != condvalue && alarm_state == true) {
                // qDebug() << item << item->row() << item->column() << "解除报警！BOOL条件不成立！";
                addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                             m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                             QString("<font color=\"blue\">报警解除，实时值=%1 (%3)，设定条件=%2</font>").arg(value).arg(condvalue).arg(typeName));
                item->setData(false, Qt::UserRole + 3);
                item->setIcon(QIcon());

                QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                if(v_chartViewPointor.isValid()) {
                    FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                    if(v_chartView) {
                        v_chartView->setAlarmFlag(false);   //  清除报警状态
                        v_chartView->setSayCount(0);    //  播放次数清零
                    }
                }
            }

        } else {
//! 其他数据类型处理过程
            double value = item->data(Qt::DisplayRole).toDouble();  //当前值
            double condvalue  = item->index().siblingAtColumn(COLUMN_CONDVALUE).data().toDouble();// 设定值
            QString condition = item->index().siblingAtColumn(COLUMN_CONDITION).data().toString();// 比较关系
            QString browname = item->index().siblingAtColumn(COLUMN_BROWNAME).data().toString();// 比较关系

            if (condition == "=") {
                // 当报警条件成立，并且上次报警状态为假时，为首次报警，发出报警信号
                if(en && value == condvalue) {
                    if(alarm_state == false) {
                        addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                                     m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                                     QString("<font color=\"red\">当前值满足设定条件，触发报警，实时值=%1 (%3)，设定条件=%2</font>").arg(value).arg(condvalue).arg(typeName));
                        item->setData(true, Qt::UserRole + 3);
                        item->setIcon(QIcon(":/alarm-1.svg"));
                    }

                    QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                    QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                    if(v_chartViewPointor.isValid()) {
                        FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                        if(v_chartView) {
                            v_chartView->setAlarmFlag(true);
                            int sc = v_chartView->getSayCount(); // 已播放次数
                            if(m_speech->state() == QTextToSpeech::Ready &&  sc < say_max_count) {
                                m_speech->say(voice_txt);
                                v_chartView->setSayCount(++sc); // 播放次数增1
                                // qDebug() << v_chartView << sc;
                            }
                        }
                    }
                }
                // 当报警条件不成立，并且上次报警状态为真时，为首次解除报警，发出解除报警信号
                if(en && value != condvalue && alarm_state == true) {
                    addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                                 m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                                 QString("<font color=\"blue\">报警解除，实时值=%1 (%3)，设定条件=%2</font>").arg(value).arg(condvalue).arg(typeName));
                    item->setData(false, Qt::UserRole + 3);
                    item->setIcon(QIcon());

                    QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                    QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                    if(v_chartViewPointor.isValid()) {
                        FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                        if(v_chartView) {
                            v_chartView->setAlarmFlag(false);
                            v_chartView->setSayCount(0);    //  播放次数清零
                        }
                    }
                }
            }
            if (condition == ">") {
                // 当报警条件成立，并且上次报警状态为假时，为首次报警，发出报警信号
                if(en && value > condvalue ) {
                    if( alarm_state == false) {
                        addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                                     m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                                     QString("<font color=\"red\">当前值满足设定条件，触发报警，实时值=%1 (%3)，设定条件&gt;%2</font>").arg(value).arg(condvalue).arg(typeName));
                        item->setData(true, Qt::UserRole + 3);
                        item->setIcon(QIcon(":/alarm-1.svg"));
                    }

                    QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                    QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                    if(v_chartViewPointor.isValid()) {
                        FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                        if(v_chartView) {
                            v_chartView->setAlarmFlag(true);
                            int sc = v_chartView->getSayCount(); // 已播放次数
                            if(m_speech->state() == QTextToSpeech::Ready &&  sc < say_max_count) {
                                m_speech->say(voice_txt);
                                v_chartView->setSayCount(++sc); // 播放次数增1
                                // qDebug() << v_chartView << sc;
                            }
                        }
                    }
                }
                // 当报警条件不成立，并且上次报警状态为真时，为首次解除报警，发出解除报警信号
                if(en && !(value > condvalue) && alarm_state == true) {
                    addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                                 m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                                 QString("<font color=\"blue\">报警解除，实时值=%1 (%3)，设定条件&gt;%2</font>").arg(value).arg(condvalue).arg(typeName));
                    item->setData(false, Qt::UserRole + 3);
                    item->setIcon(QIcon());

                    QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                    QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                    if(v_chartViewPointor.isValid()) {
                        FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                        if(v_chartView) {
                            v_chartView->setAlarmFlag(false);
                            v_chartView->setSayCount(0);    //  播放次数清零
                        }
                    }
                }
            }
            if (condition == "<") {
                // 当报警条件成立，并且上次报警状态为假时，为首次报警，发出报警信号
                if(en && value < condvalue ) {
                    if( alarm_state == false) {
                        addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                                     m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                                     QString("<font color=\"red\">当前值满足设定条件，触发报警，实时值=%1 (%3)，设定条件&lt;%2</font>").arg(value).arg(condvalue).arg(typeName));
                        item->setData(true, Qt::UserRole + 3);
                        item->setIcon(QIcon(":/alarm-1.svg"));
                    }

                    QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                    QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                    if(v_chartViewPointor.isValid()) {
                        FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                        if(v_chartView) {
                            v_chartView->setAlarmFlag(true);
                            int sc = v_chartView->getSayCount(); // 已播放次数
                            if(m_speech->state() == QTextToSpeech::Ready &&  sc < say_max_count) {
                                m_speech->say(voice_txt);
                                v_chartView->setSayCount(++sc); // 播放次数增1
                                // qDebug() << v_chartView << sc;
                            }
                        }
                    }
                }
                // 当报警条件不成立，并且上次报警状态为真时，为首次解除报警，发出解除报警信号
                if(en && !(value < condvalue) && alarm_state == true) {
                    addAlarmInfo(m_model->item(item->row(), COLUMN_BROWNAME)->data(Qt::DisplayRole).toString(),
                                 m_model->item(item->row(), COLUMN_NODEID)->data(Qt::DisplayRole).toString(),
                                 QString("<font color=\"blue\">报警解除，实时值=%1 (%3)，设定条件&lt;%2</font>").arg(value).arg(condvalue).arg(typeName));
                    item->setData(false, Qt::UserRole + 3);
                    item->setIcon(QIcon());

                    QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
                    QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
                    if(v_chartViewPointor.isValid()) {
                        FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
                        if(v_chartView) {
                            v_chartView->setAlarmFlag(false);
                            v_chartView->setSayCount(0);    //  播放次数清零
                        }
                    }
                }

            }
        }
    }

    // 当表格内容发生改变，就激活保存按钮
    if(item->column() != COLUMN_VALUE && item->column() != COLUMN_NODEID) {
        ui->btn_saveitem->setEnabled(true);
    }
    // 如果BROWNAME修改了，同步修改趋势界面
    if(item->column() == COLUMN_BROWNAME) {
        QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
        QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
        if(v_chartViewPointor.isValid()) {
            FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
            if(v_chartView) v_chartView->setBrowname(m_model->item(item->row(), COLUMN_BROWNAME)->text());
        }
    }
    // 如果限位值修改了，同步修改趋势界面
    if(item->column() == COLUMN_CONDVALUE) {
        QStandardItem *item_value = m_model->item(item->row(), COLUMN_VALUE);
        QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
        if(v_chartViewPointor.isValid()) {
            FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
            QString tmp_dd = m_model->item(item->row(), COLUMN_CONDVALUE)->data(Qt::DisplayRole).toString();
            if(tmp_dd == "true")
                tmp_dd = "1";
            else if(tmp_dd == "false")
                tmp_dd = "0";

            if(v_chartView) v_chartView->setLimit(tmp_dd.toDouble());
        }
    }

}
// 获取服务器时间
void MainWindow::subscribeOpcUaDate() {
    if(!m_client)
        return;
    QOpcUaNode *opc_date_node = m_client->node("ns=0;i=2258");
    connect(opc_date_node, &QOpcUaNode::attributeUpdated, this, [ = ](QOpcUa::NodeAttribute attr, const QVariant & value) {
        Q_UNUSED(attr);
        label_date->setText("服务器时间：" + value.toString());
    });
    connect(opc_date_node, &QOpcUaNode::disableMonitoringFinished, this, [ = ]() {
        label_date->setText("服务器时间：未连接" );
    });
    opc_date_node->enableMonitoring(QOpcUa::NodeAttribute::Value, 1000);

}

void MainWindow::refreshChartView() {
    int item_count = m_model->rowCount();
    frmChartViewN.clear();
    for(int i = 0; i < item_count; i++) {
        QStandardItem *item_browname = m_model->item(i, COLUMN_BROWNAME);
        QStandardItem *item_value = m_model->item(i, COLUMN_VALUE);
        QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
        if(v_chartViewPointor.isValid()) {
            FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
            if(v_chartView) {
                v_chartView->setPointSizeX(point_count_x);
                bool v_showFlag = item_value->data(Qt::UserRole + 5).toBool();
                if(v_showFlag) {
                    frmChartViewN << v_chartView;//加入需要显示的列表类
                    item_browname->setIcon(QIcon(":/chart1.svg"));
                } else
                    item_browname->setIcon(QIcon(""));
                v_chartView->setShowFlag(v_showFlag);
            }
        }
    }
}
void MainWindow::adjustLayout() {
    int wWidth = ui->scrollArea_chart->size().width(); // 容器的宽度
    int col_size = wWidth / (CHARTVIEW_WIDTH + CHARTVIEW_SPACING);   // 每行能容纳子组件的最大个数
    int chartViewCount = frmChartViewN.count();
    col_size = col_size <= 0 ? 1 : col_size;    // 至少要排1列
    // 在容器中自动排列子组件
    for(int k = 0 ; k < chartViewCount; k++) {
        frmChartViewN.at(k)->setGeometry(k % col_size * (CHARTVIEW_WIDTH + CHARTVIEW_SPACING), k / col_size * (CHARTVIEW_HEIGHT  + CHARTVIEW_SPACING), CHARTVIEW_WIDTH, CHARTVIEW_HEIGHT);
        // qDebug() << frmChartViewN.at(k) << k << k % col_size * (CHARTVIEW_WIDTH + CHARTVIEW_SPACING) << k / col_size * (CHARTVIEW_HEIGHT  + CHARTVIEW_SPACING);
        frmChartViewN.at(k)->setTheme(k);
    }
    int row_count = (chartViewCount % col_size) == 0 ? chartViewCount / col_size : chartViewCount / col_size + 1;
    int scr_height = row_count * (CHARTVIEW_HEIGHT + CHARTVIEW_SPACING);
    scr_height = scr_height < CHARTVIEW_HEIGHT ? ui->scrollArea_chart->size().height() : scr_height;
    ui->scrollArea_chart->widget()->resize( wWidth, scr_height);
    // ui->scrollArea_chart->setWidgetResizable(true);
    // ui->scrollArea_chart->widget()->setMinimumHeight(scr_height);
    // qDebug() << "chartViewCount" << chartViewCount << scr_height;
}


//! [4.OPCUA服务器功能]

//! [PKI Configuration]
static bool copyDirRecursively(const QString & from, const QString & to) {
    const QDir srcDir(from);
    const QDir targetDir(to);
    if (!QDir().mkpath(to))
        return false;

    const QFileInfoList infos =
        srcDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    for (const QFileInfo &info : infos) {
        const QString srcItemPath = info.absoluteFilePath();
        const QString dstItemPath = targetDir.absoluteFilePath(info.fileName());
        if (info.isDir()) {
            if (!copyDirRecursively(srcItemPath, dstItemPath))
                return false;
        } else if (info.isFile()) {
            if (!QFile::copy(srcItemPath, dstItemPath))
                return false;
        }
    }
    return true;
}
void MainWindow::setupPkiConfiguration() {
    const QDir pkidir =
        QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/pki");

    if (!pkidir.exists() && !copyDirRecursively(":/pki", pkidir.path()))
        qFatal("Could not set up directory %s!", qUtf8Printable(pkidir.path()));

    m_pkiConfig.setClientCertificateFile(pkidir.absoluteFilePath("own/certs/opcuaviewer.der"));
    m_pkiConfig.setPrivateKeyFile(pkidir.absoluteFilePath("own/private/opcuaviewer.pem"));
    m_pkiConfig.setTrustListDirectory(pkidir.absoluteFilePath("trusted/certs"));
    m_pkiConfig.setRevocationListDirectory(pkidir.absoluteFilePath("trusted/crl"));
    m_pkiConfig.setIssuerListDirectory(pkidir.absoluteFilePath("issuers/certs"));
    m_pkiConfig.setIssuerRevocationListDirectory(pkidir.absoluteFilePath("issuers/crl"));

    const QStringList toCreate = { m_pkiConfig.issuerListDirectory(),
                                   m_pkiConfig.issuerRevocationListDirectory()
                                 };
    for (const QString &dir : toCreate) {
        if (!QDir().mkpath(dir))
            qFatal("Could not create directory %s!", qUtf8Printable(dir));
    }
}
//! [PKI Configuration]


MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::resizeEvent(QResizeEvent * event) {
    QWidget::resizeEvent(event);
    adjustLayout();
}

void MainWindow::on_btn_exit_clicked() {
    close();
}


//! [5.监控-项目页面]
void MainWindow::on_btn_additem_clicked() {
    alarmDialog = new AddAlarmDialog(m_client, this);
    connect(alarmDialog, &AddAlarmDialog::addItem, this, &MainWindow::addItem);
    alarmDialog->exec();
    delete alarmDialog;
    ui->btn_saveitem->setEnabled(true);
    // ui->view_monitoring->resizeColumnsToContents();
}
void MainWindow::on_btn_saveitem_clicked() {
    saveTableView();
    ui->btn_saveitem->setEnabled(false);
}
void MainWindow::addItem(bool enable, QString browName, QString nodeId, QString condition, QString condValue, QString voiceTxt, QString interval, bool showFlag) {
    if(m_client->state() != QOpcUaClient::Connected) {
        QMessageBox::warning(this, "错误提示", "OPCUA服务器未连接！");
        return;
    }
    QList<QStandardItem*> itemList;
// column 0
    QStandardItem *t_enable = new QStandardItem("订阅");
    t_enable->setCheckable(true);
    enable ? t_enable->setCheckState(Qt::Checked) : t_enable->setCheckState(Qt::Unchecked);
    t_enable->setTextAlignment(Qt::AlignCenter);
    itemList << t_enable;
// column 1
    QStandardItem *t_browName = new QStandardItem(browName);
    itemList << t_browName;
// column 2
    QStandardItem *t_nodeId = new QStandardItem(nodeId);
    t_nodeId->setEditable(false);
    itemList << t_nodeId;
// column 3
    QStandardItem *t_value = new QStandardItem("");
    t_value->setTextAlignment(Qt::AlignCenter);
    t_value->setEditable(false);
    itemList << t_value;
// column 4
    QStandardItem *t_condition = new QStandardItem(condition);
    t_condition->setTextAlignment(Qt::AlignCenter);
    itemList << t_condition;
// column 5
    QStandardItem *t_condValue = new QStandardItem(condValue);
    t_condValue->setTextAlignment(Qt::AlignCenter);
    itemList << t_condValue;
// column 6
    itemList << new QStandardItem(voiceTxt);

    m_model->appendRow(itemList); // 将行数据加入到QStandardItemModel *model中，并显示在TableView中
    ui->view_monitoring->setCurrentIndex(m_model->indexFromItem(t_value));
    m_node = m_client->node(nodeId);    // 连接到OPCUA服务器的nodeId节点
// 为该
    FromChartView *m_chart_view = new FromChartView(ui->scrollAreaWidgetContents);
    m_chart_view->setShowFlag(false);
    m_chart_view->setBrowname(browName);
    m_chart_view->setLimit(condValue.toDouble());
    // if(showFlag) frmChartViewN << m_chart_view;

    // 将m_node对象指针转换为 variant 类型
    QVariant v_NodePointor = QVariant::fromValue((void *) m_node);
    QVariant v_chartViewPointor = QVariant::fromValue((void *) m_chart_view);
    // qDebug() << m_node << v_NodePointor;

    // 将 m_node 指针、刷新时间、趋势图指针 3个数据存放到 t_value 单元的用户数据中
    t_value->setData(v_NodePointor, Qt::UserRole + 1);
    t_value->setData(interval, Qt::UserRole + 2);
    t_value->setData(v_chartViewPointor, Qt::UserRole + 4);
    t_value->setData(showFlag, Qt::UserRole + 5);

    connect(m_node, &QOpcUaNode::attributeUpdated, this, [ = ](QOpcUa::NodeAttribute attr, const QVariant & value) {
        Q_UNUSED(attr);
        // 更新index对应单元格的内容
        m_model->setData(t_value->index(), value);

        double tmp_dd = 0.0;
        if(value.toString() == "nan")
            return;
        if(value.toString() == "true")
            tmp_dd = 1.0;
        else if(value.toString() == "false")
            tmp_dd = 0.0;
        else
            tmp_dd = value.toDouble();
        m_chart_view->addValue(tmp_dd);
        // 通知model单元格的数据发生了变化，调用setData()函数后，必须要发出dataChanged信号。
        // emit m_model->dataChanged(QModelIndex(m_index), QModelIndex(m_index));
    });
    connect(m_node, &QOpcUaNode::enableMonitoringFinished, this, [ = ](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode status) {
        Q_UNUSED(attr);
        if (!sender())
            return;
        if (status == QOpcUa::UaStatusCode::Good)
            ui->statusbar->showMessage(QString("订阅成功：%1（%2）").arg(t_browName->text(), t_nodeId->text()), 10000); // + qobject_cast<QOpcUaNode *>(sender())->nodeId(), 30000);
        else {
            ui->statusbar->showMessage(QString("订阅失败：%1（%2）").arg(t_browName->text(), t_nodeId->text()), 10000);
            qDebug() << QString("订阅失败：%1（%2）").arg(t_browName->text()).arg(t_nodeId->text()) << status;
            t_enable->setCheckState(Qt::Unchecked);
        }
    });
    connect(m_node, &QOpcUaNode::disableMonitoringFinished, this, [ = ](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode status) {
        Q_UNUSED(attr);
        if (!sender())
            return;
        if (status == QOpcUa::UaStatusCode::Good)
            ui->statusbar->showMessage("取消订阅成功。", 10000);
        else {
            ui->statusbar->showMessage("取消失败！", 10000);
            qDebug() << QString("取消失败：%1（%2）").arg(t_browName->text()).arg(t_nodeId->text()) << status;
        }
    });

    if(enable)
        m_node->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(interval.toInt()));
    refreshChartView();
}

void MainWindow::doShowChart() {
    QVariant v_itemPointor  = mContextMenuShowChartAction->data();
    if (v_itemPointor.isValid()) {
        QStandardItem *v_item = (QStandardItem*)v_itemPointor.value<void*>(); // 将指针转换为QStandardItem对象
        if (v_item) {
            bool showFlag = v_item->data(Qt::UserRole + 5).toBool();

            v_item->setData(!showFlag, Qt::UserRole + 5);

            QVariant v_chartViewPointor = v_item->data(Qt::UserRole + 4);
            FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
            v_chartView->setShowFlag(!showFlag);
            refreshChartView();
            // qDebug() << "ShowFlag" << showFlag;
        }
    }
}
// 删除监控项目
void MainWindow::on_btn_deleteitem_clicked() {
    QModelIndex current_index =  m_selection->currentIndex();
    QString str = QString("您确认要删除“第%1行：%2”记录吗？").arg(current_index.row() + 1).arg(current_index.siblingAtColumn(COLUMN_BROWNAME).data().toString());
    if(QMessageBox::question(this, "删除", str) == QMessageBox::Yes) {
        //
        QStandardItem *item_value = m_model->item(current_index.row(), COLUMN_VALUE);
        // 1.设置该页面显示标志为否
        QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
        if(v_chartViewPointor.isValid()) {
            FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
            if(v_chartView) {
                delete v_chartView;
            }
        }
        // 2.取消所选行的订阅
        QVariant t_NodePointor    = item_value->data(Qt::UserRole + 1);
        if (t_NodePointor.isValid()) {
            QOpcUaNode *t_node = (QOpcUaNode*)t_NodePointor.value<void*>(); // 将指针转换为QOpcUaNode对象
            if (t_node) {
                QOpcUaMonitoringParameters monitoring = t_node->monitoringStatus(QOpcUa::NodeAttribute::Value);
                bool Reporting = monitoring.statusCode() == QOpcUa::UaStatusCode::Good &&
                                 monitoring.monitoringMode() == QOpcUaMonitoringParameters::MonitoringMode::Reporting;
                if(Reporting) {
                    t_node->disableMonitoring(QOpcUa::NodeAttribute::Value);
                    // qDebug() << "2222:disableMonitoring";
                }
            }
        }
        // 3.删除model行
        if(current_index.row() == m_model->rowCount() - 1) // 最后一行
            m_model->removeRow(current_index.row());
        else {
            m_model->removeRow(current_index.row());
            m_selection->setCurrentIndex(current_index, QItemSelectionModel::Select);
        }
        // 4.刷新界面元素
        ui->btn_saveitem->setEnabled(true);
        refreshChartView();
    }
}
void MainWindow::clearItem() {
    while (m_model->rowCount() > 0) {
        // 取消订阅
        QStandardItem *item_value = m_model->item(0, COLUMN_VALUE);
        QVariant t_NodePointor    = item_value->data(Qt::UserRole + 1);
        if (t_NodePointor.isValid()) {
            QOpcUaNode *t_node = (QOpcUaNode*)t_NodePointor.value<void*>(); // 将指针转换为QOpcUaNode对象
            if (t_node) {
                QOpcUaMonitoringParameters monitoring = t_node->monitoringStatus(QOpcUa::NodeAttribute::Value);
                bool Reporting = monitoring.statusCode() == QOpcUa::UaStatusCode::Good &&
                                 monitoring.monitoringMode() == QOpcUaMonitoringParameters::MonitoringMode::Reporting;
                if(Reporting) {
                    // 如果已经订阅，就取消
                    t_node->disableMonitoring(QOpcUa::NodeAttribute::Value);
                }
            }
        }
        // 清除趋势图形的子组件
        QVariant v_chartViewPointor = item_value->data(Qt::UserRole + 4);
        if (v_chartViewPointor.isValid()) {
            FromChartView *v_chartView = (FromChartView*)v_chartViewPointor.value<void*>(); // 将指针转换为FromChartView对象
            if(v_chartView) delete v_chartView;
        }
        // 删除第一行
        m_model->removeRow(0);
    }
}
void MainWindow::viewToJson() {
    QSqlQuery query;
    if(query.exec("select * from tableview limit 0, 100")) {
        QJsonArray jsonArray_rows;
        while (query.next()) {
            QJsonObject jsonObj_row;
            jsonObj_row.insert("激活", query.value("激活").toBool());
            jsonObj_row.insert("监控项目", query.value("监控项目").toString());
            jsonObj_row.insert("节点", query.value("节点").toString());
            jsonObj_row.insert("报警条件", query.value("报警条件").toString());
            jsonObj_row.insert("报警值", query.value("报警值").toString());
            jsonObj_row.insert("播报内容", query.value("播报内容").toString());
            jsonObj_row.insert("刷新时间", query.value("刷新时间").toString());
            jsonObj_row.insert("显示", query.value("显示").toBool());

            jsonArray_rows.append(jsonObj_row);
        }
        QJsonDocument document;
        document.setArray(jsonArray_rows);
        QByteArray byteArray = document.toJson(QJsonDocument::Indented);
        // QString strJson(byteArray);
        // qDebug() << strJson;

        // 保存文件
        QString curPath = QDir::currentPath(),
                filter = "Json 文件|(*.json)";
        QString fName = QFileDialog::getSaveFileName(this, "保存文件", curPath + "/监控项目清单.json", filter);
        if(fName.isEmpty())
            return;

        QSaveFile jsonFile(fName);
        if(!jsonFile.open(QIODevice::WriteOnly | QIODevice::Text))
            return;
        jsonFile.setDirectWriteFallback(false);
        try {
            jsonFile.write(byteArray, byteArray.length());
            jsonFile.commit();
        } catch(QException &e) {
            jsonFile.cancelWriting();
            QMessageBox::warning(this, "错误", "保存文件出错,文件未保存!");
        }
    }
}

// 从文件中将项目清单导入
void MainWindow::viewFromJson() {
    QString curPath = QDir::currentPath(),
            filter = "Json 文件|(*.json)";
    QString fName = QFileDialog::getOpenFileName(this, "打开文件", curPath, filter);
    if(fName.isEmpty())
        return;

    QFile jsonFile(fName);
    if(!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
        return;
    QTextStream aStream(&jsonFile);
    aStream.setAutoDetectUnicode(true);
    QString str = aStream.readAll();

    QJsonParseError jsonError;
    QJsonDocument doucment = QJsonDocument::fromJson(str.toUtf8(), &jsonError);  // 转化为 JSON 文档
    if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未发生错误
        if (doucment.isArray()) { // JSON 文档为数组
            QJsonArray array = doucment.array();  // 转化为数组
            int nSize = array.size();  // 获取数组大小
            int c = 0;
            bool col1 = false, col8 = false;
            QString col2 = "", col3 = "", col4 = "", col5 = "", col6 = "", col7 = "";
            for(int i = 0; i < nSize; ++i) {  // 遍历数组
                QJsonValue jsonObj_row = array.at(i);
                if(jsonObj_row.isObject()) {
                    QJsonObject obj = jsonObj_row.toObject();
                    if(obj.contains("激活")) {
                        QJsonValue v = obj.value("激活");
                        if(v.isBool())
                            col1 = v.toBool();
                    } else continue;
                    if(obj.contains("监控项目")) {
                        QJsonValue v = obj.value("监控项目");
                        if(v.isString())
                            col2 = v.toString();
                    } else continue;
                    if(obj.contains("节点")) {
                        QJsonValue v = obj.value("节点");
                        if(v.isString())
                            col3 = v.toString();
                    } else continue;
                    if(obj.contains("报警条件")) {
                        QJsonValue v = obj.value("报警条件");
                        if(v.isString())
                            col4 = v.toString();
                    } else continue;
                    if(obj.contains("报警值")) {
                        QJsonValue v = obj.value("报警值");
                        if(v.isString())
                            col5 = v.toString();
                    } else continue;
                    if(obj.contains("播报内容")) {
                        QJsonValue v = obj.value("播报内容");
                        if(v.isString())
                            col6 = v.toString();
                    } else continue;
                    if(obj.contains("刷新时间")) {
                        QJsonValue v = obj.value("刷新时间");
                        if(v.isString())
                            col7 = v.toString();
                    } else continue;
                    if(obj.contains("显示")) {
                        QJsonValue v = obj.value("显示");
                        if(v.isBool())
                            col8 = v.toBool();
                    } else continue;

                    addItem(col1, col2,  col3,  col4,  col5,  col6, col7, col8);
                    c++;
                }
            }
            QString mes = QString("文件中共有%1条记录，成功导入%2条记录。\n注意：如需永久保存，请单击【保存】按钮。").arg(nSize).arg(c);
            QMessageBox::information(this, "提示信息", mes);
        }
    }
}

void MainWindow::on_btn_to_json_clicked() {
    viewToJson();
}
void MainWindow::on_btn_from_json_clicked() {
    viewFromJson();
    ui->btn_saveitem->setEnabled(true);
}

//! [5.监控-项目页面]


//! [6.监控-报警记录]
void MainWindow::on_btn_page_search_clicked() {
    showHistory();
}
void MainWindow::on_spinBox_rec_count_valueChanged(int arg1) {
    history_rec_count_page = arg1;
}
void MainWindow::on_btn_page_frist_clicked() {
    history_current_page = 1;
    showHistory();
}
void MainWindow::on_btn_page_pre_clicked() {
    history_current_page--;
    showHistory();
}
void MainWindow::on_btn_page_next_clicked() {
    history_current_page++;
    showHistory();
}
void MainWindow::on_btn_page_end_clicked() {
    history_current_page = history_pages;
    showHistory();
}
//! [6.监控-报警记录]

void MainWindow::on_btn_point_count_x_clicked() {
    QSqlQuery query;
    query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'point_count_x'").arg(ui->spinBox_point_count_x->text()));
    ui->btn_point_count_x->setEnabled(false);
}


void MainWindow::on_spinBox_point_count_x_valueChanged(int arg1) {
    ui->btn_point_count_x->setEnabled(true);
    point_count_x = arg1;
}


void MainWindow::on_stackedWidget_currentChanged(int arg1) {
    // qDebug() << "Page:" << arg1;
    for(int i = 0; i < frmChartViewN.count(); i++)
        if(arg1 == 4) //Chart显示页面,只有当前页为ChartView页面时，才刷新曲线图，降低CPU占用率
            frmChartViewN.at(i)->setShowFlag(true);
        else
            frmChartViewN.at(i)->setShowFlag(false);
}

void MainWindow::on_spinBox_say_max_count_valueChanged(int arg1) {
    say_max_count = arg1;
    ui->btn_say_max_count_save->setEnabled(true);
}


void MainWindow::on_btn_say_max_count_save_clicked() {
    QSqlQuery query;
    query.exec(QString("UPDATE sysinfo SET 值='%1' WHERE 参数 = 'say_max_count'").arg(ui->spinBox_say_max_count->text()));
    ui->btn_say_max_count_save->setEnabled(false);
}

